Micron Document
🎖️GitЯра🎖️

Commit 1aa8c51cd67bec3958a5b63a15641b08f8959b37


Parents : d8b6dd7
Author : copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Signature : Signature validation error
Date : 2026-05-31T14:35:35Z
Committer : GitHub <noreply@github.com>
Date : 2026-05-31T14:35:35Z

feat: save unsent chat message as draft across navigation

Store the message input text in the ViewModel (backed by SavedStateHandle)
so it persists when navigating away and returning to the chat screen.

- Add draftMessage StateFlow to MessageViewModel
- Initialize TextFieldState from ViewModel draft when route message is empty
- Sync text field changes back to ViewModel via snapshotFlow
- Clear draft when message is successfully sent

Changes
Diff

diff --git a/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt b/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt
index 89ac0ef45b..12adec5341 100644
--- a/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt
+++ b/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/Message.kt
@@ -51,6 +51,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
@@ -138,12 +139,19 @@ fun MessageScreen(
var showDeleteDialog by rememberSaveable { mutableStateOf(false) }
var sharedContact by rememberSaveable { mutableStateOf<Node?>(null) }
val selectedMessageIds = rememberSaveable { mutableStateOf(emptySet<Long>()) }
- val messageInputState = rememberTextFieldState(message)
+ val draft by viewModel.draftMessage.collectAsStateWithLifecycle()
+ val messageInputState = rememberTextFieldState(message.ifEmpty { draft })
val showQuickChat by viewModel.showQuickChat.collectAsStateWithLifecycle()
val filteredCount by viewModel.filteredCount.collectAsStateWithLifecycle()
val showFiltered by viewModel.showFiltered.collectAsStateWithLifecycle()
val filteringDisabled = contactSettings[contactKey]?.filteringDisabled ?: false
+ // Sync text field changes back to ViewModel draft
+ LaunchedEffect(messageInputState) {
+ snapshotFlow { messageInputState.text.toString() }
+ .collect { text -> viewModel.setDraftMessage(text) }
+ }
+
// Prevent the message TextField from stealing focus when the screen opens
LaunchedEffect(contactKey) { focusManager.clearFocus() }
@@ -233,6 +241,7 @@ fun MessageScreen(
viewModel.sendMessage(event.text, contactKey, event.replyingToPacketId)
if (event.replyingToPacketId != null) replyingToPacketId = null
messageInputState.clearText()
+ viewModel.clearDraftMessage()
}
is MessageScreenEvent.SendReaction ->

diff --git a/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt b/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt
index ca29b38421..aeaa62a3e4 100644
--- a/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt
+++ b/feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt
@@ -70,6 +70,19 @@ class MessageViewModel(
private val _title = MutableStateFlow("")
val title: StateFlow<String> = _title.asStateFlow()
+ private val _draftMessage = MutableStateFlow(savedStateHandle.get<String>("draftMessage") ?: "")
+ val draftMessage: StateFlow<String> = _draftMessage.asStateFlow()
+
+ fun setDraftMessage(text: String) {
+ _draftMessage.value = text
+ savedStateHandle["draftMessage"] = text
+ }
+
+ fun clearDraftMessage() {
+ _draftMessage.value = ""
+ savedStateHandle["draftMessage"] = ""
+ }
+
val ourNodeInfo = nodeRepository.ourNodeInfo
val connectionState = serviceRepository.connectionState

Served by rngit 1.5.2 - Generated in 0.13s